package com.chinacoal.hr.levelimpl.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinacoal.hr.levelapi.entity.AttendanceInfo;
import com.chinacoal.hr.levelapi.entity.AttendanceInfoTemp;
import com.chinacoal.hr.levelapi.entity.Employee;
import com.chinacoal.hr.levelimpl.dao.AttendanceInfoDao;
import com.chinacoal.hr.levelimpl.service.AttendanceInfoService;
import com.chinacoal.hr.levelimpl.service.AttendanceInfoTempService;
import com.chinacoal.hr.levelimpl.service.AttendanceSummaryService;
import com.chinacoal.hr.levelimpl.service.EmployeeService;
import com.chinacoal.hr.levelimpl.utils.DateUtil;
import com.chinacoal.hr.levelimpl.utils.StaticDicts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author: YunTao.Li
 * @create: 2019-11-13 17:21
 * @description:
 **/
@Service
public class AttendanceInfoServiceImpl implements AttendanceInfoService {
    @Value("${prop.sync-thread-pool}")
    private int THREAD_POLL;

    private Logger logger = LoggerFactory.getLogger(AttendanceInfoServiceImpl.class);

    @Autowired
    private AttendanceInfoDao attendanceInfoDao;

    @Autowired
    private AttendanceInfoTempService attendanceInfoTempService;

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private AttendanceSummaryService attendanceSummaryService;

    /**
     * 查询考勤信息列表
     */
    public IPage<AttendanceInfo> getAttendanceInfoPage(Map<String, Object> condition) throws Throwable {
        IPage<AttendanceInfo> attendanceInfoIPage = attendanceInfoDao.getAttendanceInfoPage(condition);

        return attendanceInfoIPage;
    }

    /**
     * 查询某人某年月的考勤异常信息
     *
     * @param condition :
     * @return : com.baomidou.mybatisplus.core.metadata.IPage<com.chinacoal.hr.levelimpl.entity.AttendanceInfo>
     * @author : YunTao.Li
     * @date : 2019/11/21 2019/11/21
     */
    public IPage<AttendanceInfo> getUnusualAttendanceInfoPage(Map<String, Object> condition) {
        IPage<AttendanceInfo> attendanceInfoIPage = attendanceInfoDao.getAttendanceInfoPage(condition);

        int recordsSize = attendanceInfoIPage.getRecords().size();

        // 初始化返回的异常IPage与list
        IPage<AttendanceInfo> exceptionInfo = new Page<>();
        List<AttendanceInfo> attendanceList = new ArrayList<>();

        /**
         *  step1 ：
         *  遍历取回的异常考勤信息
         *  如果一条数据只有sign_up没有sign_out 则为一条下午异常
         *  如果一条数据既没有sign_up也没有sign_out 则拆分为两条记录上午异常跟下午异常
         */
        for (int i = 0; i < recordsSize; i++) {
            AttendanceInfo info = attendanceInfoIPage.getRecords().get(i);

            Object signUpObj = info.getSignUp();
            Object signOutObj = info.getSignOut();
            String signUpStatus = info.getSignUpStatus();
            String signOutStatus = info.getSignOutStatus();

            /**
             * step2.1 : 判断是否上午异常
             */
            if (!StaticDicts.SIGN_STATUS_NORMAL.equals(signUpStatus)) { //判断如果不是StaticDicts.SIGN_STATUS_NORMAL != '0' 那么就是不正常
                // 上午数据
                AttendanceInfo signUpExceptionInfo = new AttendanceInfo();
                // 拷贝方法  info 源对象   signUpExceptionInfo 目标对象
                BeanUtils.copyProperties(info, signUpExceptionInfo);
                Integer signUp = signUpExceptionInfo.getSignUp();
                if (signUp == null) {
                    signUp = 0;
                }
                signUpExceptionInfo.setSignDate(signUp);//实际打卡时间
                signUpExceptionInfo.setTimeType(StaticDicts.TIME_TYPE_MORNING);   // StaticDicts.TIME_TYPE_MORNING == '1' 上午
                signUpExceptionInfo.setSignStatus(signUpExceptionInfo.getSignUpStatus());
                attendanceList.add(signUpExceptionInfo);

            }

            /**
             * step2.2 : 判断是否下午异常
             */
            if (!StaticDicts.SIGN_STATUS_NORMAL.equals(signOutStatus)) {  //判断如果不是StaticDicts.SIGN_STATUS_NORMAL != '0' 那么就是不正常
                //下午数据
                AttendanceInfo signOutExceptionInfo = new AttendanceInfo();
                BeanUtils.copyProperties(info, signOutExceptionInfo);
                Integer signOut = signOutExceptionInfo.getSignOut();
                if (signOut == null) {
                    signOut = 0;
                }
                signOutExceptionInfo.setSignDate(signOut); //实际打卡时间
                signOutExceptionInfo.setTimeType(StaticDicts.TIME_TYPE_AFTERNOON);   //StaticDicts.TIME_TYPE_AFTERNOON== '2' 下午
                signOutExceptionInfo.setSignStatus(signOutExceptionInfo.getSignOutStatus());
                attendanceList.add(signOutExceptionInfo);
            }
        }
        exceptionInfo.setRecords(attendanceList);
        return exceptionInfo;
    }

    /**
     * 获得考勤信息明细list
     *
     * @param beginDate :
     * @param endDate   :
     * @return : java.util.List<com.chinacoal.hr.levelimpl.entity.AttendanceInfo>
     * @author : YunTao.Li
     * @date : 2020/7/6 2020/7/6
     */
    @Override
    public List<AttendanceInfo> getAttendanceInfoList(String beginDate, String endDate) throws Throwable {
        Map<String, Object> condition = new HashMap<String, Object>();
        condition.put("beginDate", beginDate);
        condition.put("endDate", endDate);

        return this.attendanceInfoDao.getAttendanceInfoList(condition);
    }

    /**
     * 同步考勤信息
     *
     * @param beginDateStr :yyyyMMdd
     * @param endDateStr   :yyyyMMdd
     * @return : java.util.Map<java.lang.String,java.lang.String>
     * @author : YunTao.Li
     * @date : 2019/12/22 2019/12/22
     */
    @Transactional
    public Map<String, String> syncAttendanceInfo(String beginDateStr, String endDateStr) throws Throwable {
        logger.info("开始同步- beginDateStr :" + beginDateStr + " endDateStr:" + endDateStr);
        long begin = System.currentTimeMillis();

        // step1 : 同步考勤临时表信息
        List<AttendanceInfoTemp> attendanceInfoTemps = this.attendanceInfoTempService.syncAttendanceInfoTemp(beginDateStr, endDateStr);

        // step3 : 遍历临时考勤信息
        for (int i = 0; i < attendanceInfoTemps.size(); i++) {
            AttendanceInfoTemp tempAttendance = attendanceInfoTemps.get(i);
            // step3.1 : 获得考勤日期，并覆盖当天的正式考勤记录
            Date attendDate = tempAttendance.getAttendDate();
            String empCode = tempAttendance.getEmpCode();
            if (("10010786").equals(empCode)) {
                System.out.println(1);
            }
            this.coverAttendanceInfo(empCode, attendDate, tempAttendance);
        }

        //step4 : 统计考勤汇总记录
        /**
         * 同步不统计汇总
         *
         *   SimpleDateFormat simple = new SimpleDateFormat("yyyyMMdd");
         *   SimpleDateFormat simple2 = new SimpleDateFormat("yyyy-MM");
         *   String yrmonth = "";
         *   try {
         *       yrmonth = simple2.format(simple.parse(beginDateStr));
         *   } catch (ParseException e) {
         *       e.printStackTrace();
         *   }
         *
         *   attendanceSummaryService.attendanceSummaryRolling(yrmonth);
         * */

        long time = System.currentTimeMillis() - begin;
        System.out.println("同步完成,耗时" + time);
        return null;
    }

    /**
     * 高并发,多线程同步
     *
     * @param beginDateStr :
     * @param endDateStr   :
     * @param factor       :
     * @return : java.util.Map<java.lang.String,java.lang.String>
     * @author : YunTao.Li
     * @date : 2020/1/7 2020/1/7
     */
    @Override
    public Map<String, String> concurrentSyncAttendanceInfo(String beginDateStr, String endDateStr, int factor) throws Throwable {
        ExecutorService threadPoolService = Executors.newFixedThreadPool(THREAD_POLL); // 初始化线程池

        try {
            List<Map<String, String>> dateSplitMapList = DateUtil.getDaySplit(beginDateStr, endDateStr, factor);  //几天一分
            for (int i = 0; i < dateSplitMapList.size(); i++) {
                Map<String, String> dateSplitMap = dateSplitMapList.get(i);
                String splitStartDateStr = dateSplitMap.get("startDateStr");
                String splitEndDateStr = dateSplitMap.get("endDateStr");

                Callable call = new ConcurrentSyncAttendanceInfoCallable(splitStartDateStr, splitEndDateStr, this);
                Future future = threadPoolService.submit(call);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        threadPoolService.shutdown();
        while (!threadPoolService.awaitTermination(1, TimeUnit.SECONDS)) {
            System.out.println("等待线程池执行完毕");
        }

        System.out.println("线程池执行完毕! 同步成功:beginDateStr :" + beginDateStr + " endDateStr:" + endDateStr + " factor:" + factor);

        return null;
    }

    /**
     * 覆盖正式的考勤记录
     *
     * @param empCode    :
     * @param attendDate :
     * @return : java.util.Map<java.lang.String,java.lang.String>
     * @author : YunTao.Li
     * @date : 2019/12/26 2019/12/26
     */
    @Override
    public Map<String, String> coverAttendanceInfo(String empCode, Date attendDate, AttendanceInfoTemp attendanceInfoTemp) throws Throwable {
        try {
            // step1 : 删除记录
            Map<String, Object> deleteCondition = new HashMap<String, Object>();
            deleteCondition.put("empCode", empCode);
            deleteCondition.put("attendDate", attendDate);
            this.attendanceInfoDao.delete(deleteCondition);

            // step2 : 将临时考勤属性copy给正式考勤信息
            AttendanceInfo attendanceInfo = new AttendanceInfo();
            // 拷贝方法 attendanceInfoTemp 源对象  attendanceInfo  目标对象
            BeanUtils.copyProperties(attendanceInfoTemp, attendanceInfo);

            // step3 : 根据empCode获得其他的人员基本信息包括该人员机构、部门、姓名等
            Employee employee = employeeService.getEmployeeDetailLocal(empCode);


            // 如果对象为空说明本地库里没有此对象，记录日志
            if (employee != null) {
                String empName = employee.getEmpName();
                String deptCode = employee.getDeptCode();
                String deptName = employee.getDeptName();
                String orgCode = employee.getOrgCode();
                String orgName = employee.getOrgName();

                attendanceInfo.setEmpName(empName);
                attendanceInfo.setDeptCode(deptCode);
                attendanceInfo.setDeptName(deptName);
                attendanceInfo.setOrgCode(orgCode);
                attendanceInfo.setOrgName(orgName);
            } else {
                String msg = "进行考勤同步动作,AttendanceInfoServiceImpl类调用coverAttendanceInfo,没有在本地库中找到对应的employee对象,empCode为:" + empCode;
//                logger.info(msg);
                return null;
            }

            // step4 : 属性优化
            // step4.1 : 判断考勤状态
            String signUpStatus = attendanceInfoTemp.getSignUpStatus();
            String signOutStatus = attendanceInfoTemp.getSignOutStatus();
            String confirmStatus = "N";
            if (StaticDicts.SIGN_STATUS_NORMAL.equals(signUpStatus) && StaticDicts.SIGN_STATUS_NORMAL.equals(signOutStatus)) {
                confirmStatus = "Y";
            }

            attendanceInfo.setConfirmStatus(confirmStatus);
            // step2 : 插入新的记录
            this.attendanceInfoDao.insert(attendanceInfo);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据条件获得考勤记录
     *
     * @param empCode    :
     * @param attendDate :
     * @return : com.chinacoal.hr.levelimpl.entity.AttendanceInfo
     * @author : YunTao.Li
     * @date : 2020/7/24 2020/7/24
     */
    @Override
    public AttendanceInfo getAttendanceInfo(String empCode, Date attendDate) throws Throwable {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String attendDateStr = format.format(attendDate);

        Map condition = new HashMap();
        condition.put("empCode", empCode);
        condition.put("attendDate", attendDateStr);

        AttendanceInfo attendanceInfo = this.attendanceInfoDao.getAttendanceInfo(condition);
        return attendanceInfo;
    }

    @Override
    public Map<String, String> updateAttendanceInfo(AttendanceInfo attendanceInfo) throws Throwable {
        this.attendanceInfoDao.updateAttendanceInfo(attendanceInfo);
        return null;
    }

    /**
     * 根据出差、请假、申诉记录来更新考勤明细数据
     *
     * @param empCode    : 员工code
     * @param attendDate : 日期
     * @param timeType   :
     * @param paType     :
     * @return : java.util.Map<java.lang.String,java.lang.String>
     * @author : YunTao.Li
     * @date : 2020/7/26 2020/7/26
     */
    @Override
    public Map<String, String> attendanceInfoFixedByProcess(String empCode, Date attendDate, String timeType, String paType) throws Throwable {
        AttendanceInfo attendanceInfo = this.getAttendanceInfo(empCode, attendDate);

        /**
         * 如果有审批记录而没有考勤记录有可能是面向未来的审批,比如今天批明天的请假记录
         * */
        if (attendanceInfo == null) {
            return null;
        }
        attendanceInfo.setHolidayType(paType); // 这里实际上是patype

        if (StaticDicts.TIME_TYPE_MORNING.equals(timeType)) { // 上午

            attendanceInfo.setSignUpStatus(StaticDicts.SIGN_STATUS_NORMAL);
            if (StaticDicts.SIGN_STATUS_NORMAL.equals(attendanceInfo.getSignOutStatus())) {
                attendanceInfo.setConfirmStatus(StaticDicts.CONFIRM_STATUS_NORMAL);
            }

        } else if (StaticDicts.TIME_TYPE_AFTERNOON.equals(timeType)) { // 下午
            attendanceInfo.setSignOutStatus(StaticDicts.SIGN_STATUS_NORMAL);

            if (StaticDicts.SIGN_STATUS_NORMAL.equals(attendanceInfo.getSignUpStatus())) {
                attendanceInfo.setConfirmStatus(StaticDicts.CONFIRM_STATUS_NORMAL);
            }

        } else if (StaticDicts.TIME_TYPE_ALLDAY.equals(timeType)) { // 全天
            attendanceInfo.setSignUpStatus(StaticDicts.SIGN_STATUS_NORMAL);
            attendanceInfo.setSignOutStatus(StaticDicts.SIGN_STATUS_NORMAL);
            attendanceInfo.setConfirmStatus(StaticDicts.CONFIRM_STATUS_NORMAL);
        }

        this.updateAttendanceInfo(attendanceInfo);

        return null;
    }
}


/**
 * 内部类,多线程调用
 *
 * @author : YunTao.Li
 * @date : 2020/1/7 2020/1/7
 * @return : null
 *   实现Callable 接口 重写call 方法
 */
class ConcurrentSyncAttendanceInfoCallable implements Callable {
    private String startDateStr;
    private String endDateStr;
    private AttendanceInfoService attendanceInfoService;

    public ConcurrentSyncAttendanceInfoCallable(String startDateStr, String endDateStr, AttendanceInfoService obj) {
        this.startDateStr = startDateStr;
        this.endDateStr = endDateStr;
        this.attendanceInfoService = obj;
    }

    public Object call() throws Exception {
        try {
            attendanceInfoService.syncAttendanceInfo(startDateStr, endDateStr);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return true;
    }
}